package chaincode

import (
	"encoding/json"
	"fmt"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
	contractapi.Contract
}

// Asset describes basic details of what makes up a simple asset
// Insert struct field in alphabetic order => to achieve determinism across languages
// golang keeps the order when marshal to json but doesn't order automatically
type Contract struct {
	ID           string `json:"id"`
	Tenant       string `json:"tenant"`
	Landlord     string `json:"landlord"`
	HouseAddress string `json:"house_address"`
	Contract     string `json:"contract"`
}

// InitLedger adds a base set of assets to the ledger
func (s *SmartContract) initLedger(ctx contractapi.TransactionContextInterface) error {
	contracts := []Contract{
		{ID: "asset1", Tenant: "佳欣", Landlord: "大钊", HouseAddress: "上海市", Contract: "123456789"},
		{ID: "asset2", Tenant: "新伟", Landlord: "加薪", HouseAddress: "上海市", Contract: "123456789"},
		{ID: "asset3", Tenant: "天空", Landlord: "大地", HouseAddress: "上海市", Contract: "123456789"},
		{ID: "asset4", Tenant: "天堂", Landlord: "地狱", HouseAddress: "上海市", Contract: "123456789"},
		{ID: "asset5", Tenant: "佳欣", Landlord: "大钊", HouseAddress: "上海市", Contract: "123456789"},
	}

	for _, contract := range contracts {
		contractJSON, err := json.Marshal(contract)
		if err != nil {
			return err
		}

		err = ctx.GetStub().PutState(contract.ID, contractJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}

	return nil
}

// 获取所有合同信息
// GetAllAssets returns all assets found in world state
func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Contract, error) {
	// range query with empty string for startKey and endKey does an
	// open-ended query of all assets in the chaincode namespace.
	resultsIterator, err := ctx.GetStub().GetStateByRange("", "")
	if err != nil {
		return nil, err
	}
	defer resultsIterator.Close()

	var contracts []*Contract

	for resultsIterator.HasNext() {
		queryResponse, err := resultsIterator.Next()
		if err != nil {
			return nil, err
		}

		var contract Contract

		err = json.Unmarshal(queryResponse.Value, &contract)
		if err != nil {
			return nil, err
		}
		contracts = append(contracts, &contract)
	}
	return contracts, nil
}

// 批量添加
func (s *SmartContract) BulkAddContract(ctx contractapi.TransactionContextInterface, args string) error {
	var contracts []*Contract
	err := json.Unmarshal([]byte(args), &contracts)
	if err != nil {
		return err
	}

	for _, contract := range contracts {
		contractJSON, err := json.Marshal(contract)
		if err != nil {
			return err
		}

		err = ctx.GetStub().PutState(contract.ID, contractJSON)
		if err != nil {
			return fmt.Errorf("failed to put to world state. %v", err)
		}
	}
	return nil
}

// CreateAsset issues a new asset to the world state with given details.
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, tenant string, landlord string, houseaddress string, url string) error {
	// 更具ID判断当前合同是否存在
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	// 如果存在则返回错误
	if exists {
		return fmt.Errorf(" 合同  %s  已经存在", id)
	}
	// 将数据放入结构体
	asset := Contract{
		ID:           id,
		Tenant:       tenant,
		Landlord:     landlord,
		HouseAddress: houseaddress,
		Contract:     url,
	}
	// 转换成json字符串
	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}

	// 将数据存入世界状态
	return ctx.GetStub().PutState(id, assetJSON)
}

// ReadAsset returns the asset stored in the world state with given id.
func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Contract, error) {

	contractJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return nil, fmt.Errorf("从世界状态读取失败: %v", err)
	}

	if contractJSON == nil {
		return nil, fmt.Errorf("合同 %s 不存在", id)
	}

	// 将读取到的数据转换成结构体
	var contract Contract

	err = json.Unmarshal(contractJSON, &contract)
	if err != nil {
		return nil, err
	}
	//返回结构体
	return &contract, nil
}

// UpdateAsset 使用提供的参数更新处于世界状态的现有资产。
func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, tenant string, landlord string, houseaddress string, contract string) error {
	// 判断合同id是否存在
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("合同 %s 不存在", id)
	}

	// 用新资产覆盖原有资产
	asset := Contract{
		ID:           id,
		Tenant:       tenant,
		Landlord:     landlord,
		HouseAddress: houseaddress,
		Contract:     contract,
	}

	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return err
	}

	return ctx.GetStub().PutState(id, assetJSON)
}

// DeleteAsset deletes an given asset from the world state.
func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error {
	exists, err := s.AssetExists(ctx, id)
	if err != nil {
		return err
	}
	if !exists {
		return fmt.Errorf("the asset %s does not exist", id)
	}

	return ctx.GetStub().DelState(id)
}

// AssetExists returns true when asset with given ID exists in world state
func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) {
	assetJSON, err := ctx.GetStub().GetState(id)
	if err != nil {
		return false, fmt.Errorf("failed to read from world state: %v", err)
	}

	return assetJSON != nil, nil
}

// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, contract string) (string, error) {
	asset, err := s.ReadAsset(ctx, id)
	if err != nil {
		return "", err
	}

	oldContract := asset.Landlord
	asset.Contract = contract

	assetJSON, err := json.Marshal(asset)
	if err != nil {
		return "", err
	}

	err = ctx.GetStub().PutState(id, assetJSON)
	if err != nil {
		return "", err
	}
	return oldContract, nil
}
